﻿using Microsoft.Data.SqlClient;

namespace OpenPms.Services;

public partial class OrderService
{
    public async Task<Guid> CreateReservationAsync(TReservation q)
    {

        var pQuantity = new SqlParameter("@Quantity", q.Quantity)
        {
            Direction = System.Data.ParameterDirection.InputOutput
        };
        var pRoomTypeName = new SqlParameter("@RoomTypeName", System.Data.SqlDbType.NVarChar, 32)
        {
            Direction = System.Data.ParameterDirection.Output
        };
        await db.Database.ExecuteSqlInterpolatedAsync($"TryAddQuantity {q.HotelId},{q.RoomTypeId},{DateOnly.FromDateTime(q.DTCheckIn)},{DateOnly.FromDateTime(q.DTCheckOut)},{pQuantity} output,{pRoomTypeName} output");


        if ((int)pQuantity.Value < q.Quantity)
        {
            await SendFailCreateReservationWXMsgAsync(q, (int)pQuantity.Value);
            throw new ApiException(EResponseCode.房量不足, $"{pRoomTypeName.Value}只剩{pQuantity.Value}间");
        }

        if (q.Id == Guid.Empty)
            q.Id = Guid.NewGuid();
        q.RoomTypeName = (string)pRoomTypeName.Value;

        db.Reservations.Add(q);
        await db.SaveChangesAsync();
        await SendSuccessCreateReservationWXMsgAsync(q);
        return q.Id;

    }
    public async Task<Guid> CancelReservationAsync(RequestCancelReservation q)
    {
        if (q.ReservationId is null && string.IsNullOrEmpty(q.OtaOrderId))
            throw new ApiException(EResponseCode.订单号不能为空);
        TReservation? item;
        if (q.ReservationId is null)
            item = await db.Reservations.Where(ii => ii.HotelId == q.HotelId && ii.OtaOrderId == q.OtaOrderId).FirstOrDefaultAsync();
        else
            item = await db.Reservations.Where(ii => ii.Id == q.ReservationId && ii.HotelId == q.HotelId).FirstOrDefaultAsync();

        if (item is null)
            throw new ApiException(EResponseCode.订单不存在);

        if (await db.Orders.AnyAsync(ii => ii.HotelId == q.HotelId && ii.ReservationId == q.ReservationId && ii.OrderStatus != EOrderStatus.登记))
            throw new ApiException(EResponseCode.已经有人入住);

        db.ArchiveReservations.Add(item.Cancel(q.Reason));

        try
        {
            await db.SaveChangesAsync();
        }
        catch (DbUpdateException ex)
        {
            logger.LogError(ex.ToString());
            throw new ApiException(EResponseCode.并发错误, ex.Message + ex.InnerException?.Message);
        }

        var dateIn = DateOnly.FromDateTime(item.DTCheckIn);
        var dateOut = DateOnly.FromDateTime(item.DTCheckOut);
        await db.Quantities.Where(ii => ii.HotelId == q.HotelId && ii.RoomTypeId == item.RoomTypeId && ii.Date >= dateIn && ii.Date < dateOut).ExecuteUpdateAsync(ii => ii.SetProperty(jj => jj.OutOfOrder, jj => jj.OutOfOrder + item.Quantity));
        var roomNos = await db.Orders.Where(ii => ii.ReservationId == q.ReservationId).Select(ii => ii.RoomNo).Distinct().ToListAsync();
        if (roomNos.Count > 0)
            await db.Rooms.Where(ii => ii.HotelId == q.HotelId && roomNos.Contains(ii.RoomNo)).ExecuteUpdateAsync(ii => ii.SetProperty(jj => jj.RoomStatus, jj => ERoomStatus.空));
        await db.Reservations.Where(ii => ii.Id == item.Id).ExecuteDeleteAsync();
        if (!string.IsNullOrEmpty(q.OtaOrderId))
            await SendCancelWXMsgAsync(item);

        return item.Id;

    }

    //private async Task<Guid> GetMainOrderIdAsync(int hotelId, Guid? orderId, string? otaOrderId = null)
    //{
    //    Guid mainOrderId;
    //    if (orderId.HasValue)
    //        mainOrderId = await db.Orders.Where(ii => ii.Id == orderId && ii.HotelId == hotelId).Select(ii => ii.MainOrderId).FirstOrDefaultAsync();
    //    else if (!string.IsNullOrEmpty(otaOrderId))
    //        mainOrderId = await db.Orders.Where(ii => ii.HotelId == hotelId && ii.OtaOrderId == otaOrderId).Select(ii => ii.MainOrderId).FirstOrDefaultAsync();
    //    else
    //        throw new ApiException(EResponseCode.订单号不能为空);
    //    if (mainOrderId == Guid.Empty)
    //        throw new ApiException(EResponseCode.订单号不存在);
    //    return mainOrderId;
    //}


    public async Task<List<TReservation>> QueryReservationAsync(int hotelId, Guid? reservationId, string? mobile, string? name)
    {
        List<TReservation>? items = null;
        if (reservationId.HasValue)
            items = await db.Reservations.Where(ii => ii.Id == reservationId.Value && ii.HotelId == hotelId).ToListAsync();
        if (items is null && !string.IsNullOrEmpty(mobile))
            items = await db.Reservations.Where(ii => ii.HotelId == hotelId && ii.ContactMobile == mobile).ToListAsync();
        if (items is null && !string.IsNullOrEmpty(name))
            items = await db.Reservations.Where(ii => ii.HotelId == hotelId && !string.IsNullOrEmpty(ii.ContactName) && ii.ContactName.Contains(name)).ToListAsync();
        if (items?.Count > 0)
            return items;
        else
            return new();
    }

    public async Task<TReservation> RegisterAsync(int hotelId, Guid reservationId, bool needArrange = true)
    {
        if (!needArrange)
            return await RegisterOnlyAsync(hotelId, reservationId);
        else
            return await RegisterThenArrangeAsync(hotelId, reservationId);
    }
    private async Task<TReservation> RegisterThenArrangeAsync(int hotelId, Guid reservationId, int retry = 0)
    {
        var item = await db.Reservations.AsTracking().Include(ii => ii.Accounts).Include(ii => ii.Orders).FirstOrDefaultAsync(ii => ii.Id == reservationId && ii.HotelId == hotelId);
        if (item == null)
            throw new ApiException(EResponseCode.订单不存在);
        if (item.Orders.Count > 0)
            return item;
        var rooms = await db.Rooms.AsTracking().Where(ii => ii.HotelId == hotelId && ii.RoomTypeId == item.RoomTypeId && ii.RoomStatus == ERoomStatus.空).OrderBy(ii => new { ii.IsDirty, ii.ArrangeIndex }).Take(item.Quantity).ToListAsync();
        for (int i = 0; i < item.Quantity; i++)
        {
            if (i > rooms.Count)
                item.Orders.Add(new TOrder(item.RoomTypeId, item.RoomTypeName, item.DTCheckIn, item.DTCheckOut, item.Memo, item.Id));
            else
            {
                rooms[i].RoomStatus = ERoomStatus.预;
                item.Orders.Add(new TOrder(item.RoomTypeId, item.RoomTypeName, item.DTCheckIn, item.DTCheckOut, item.Memo, item.Id, rooms[i].RoomNo, rooms[i].IsDirty));
            }
        }
        //item.OrderStatus = EOrderStatus.登记;
        try
        {
            await db.SaveChangesAsync();
            return item;
        }
        catch (DbUpdateConcurrencyException ex)
        {
            if (retry < 5)
            {
                await db.Entry(item).ReloadAsync();
                foreach (var room in rooms)
                    await db.Entry(room).ReloadAsync();
                return await RegisterThenArrangeAsync(hotelId, reservationId, retry + 1);
            }
            logger.LogError(ex.ToString());
            throw new ApiException(EResponseCode.并发错误);
        }

    }
    private async Task<TReservation> RegisterOnlyAsync(int hotelId, Guid reservationId, int retry = 0)
    {
        var item = await db.Reservations.AsTracking().Include(ii => ii.Accounts).Include(ii => ii.Orders).FirstOrDefaultAsync(ii => ii.Id == reservationId && ii.HotelId == hotelId);
        if (item == null)
            throw new ApiException(EResponseCode.订单不存在);
        if (item.Orders.Count > 0)
            return item;
        var rateItem = await db.Rates.FindAsync(item.RateId);
        if (rateItem == null || !rateItem.IsPublish)
            throw new ApiException(EResponseCode.房价码错误);
        DateTime dtCheckout = rateItem.RateType == ERateType.钟点房 ? item.DTCheckOut : DateOnly.FromDateTime(item.DTCheckOut).ToDateTime(rateItem.CheckoutTime);

        for (int i = 0; i < item.Quantity; i++)
            item.Orders.Add(new TOrder(item.RoomTypeId, item.RoomTypeName, item.DTCheckIn, dtCheckout, item.Memo, item.Id));

        try
        {
            await db.SaveChangesAsync();
            return item;
        }
        catch (DbUpdateConcurrencyException ex)
        {
            if (retry < 5)
            {
                db.Entry(item).Reload();
                item.Orders.Clear();
                return await RegisterOnlyAsync(hotelId, reservationId, retry + 1);
            }
            logger.LogError(ex.ToString());
            throw new ApiException(EResponseCode.并发错误);
        }

    }

}
